import os
from typing import Iterable, List, Set, Any

from Server import logger, var, const

# 配置规则
ConfigRole = {
    'valueType': dict,
    'value': {
        'server': {
            'valueType': dict,
            'value': {
                'ip': {
                    'valueType': str
                },
                'port': {
                    'valueType': int
                },
                'proxy': {
                    'valueType': dict,
                    'value': {
                        'en': {
                            'valueType': bool
                        },
                        'http_port': {
                            'valueType': int
                        },
                        'url': {
                            'valueType': str
                        },
                        'real_ip_header': {
                            'valueType': str
                        }
                    }
                }
            }
        },
        'log': {
            'valueType': dict,
            'value': {
                'en': {
                    'valueType': bool
                },
                'path': {
                    'valueType': str
                },
                'con': {
                    'valueType': bool
                }
            }
        },
        'hidden': {
            'valueType': dict,
            'value': {
                'types': {
                    'valueType': list,
                    'each': {
                        'valueType': str
                    }
                },
                'dirs': {
                    'valueType': list,
                    'each': {
                        'valueType': str
                    }
                },
                'files': {
                    'valueType': list,
                    'each': {
                        'valueType': str
                    }
                }
            }
        },
        'route_path': {
            'valueType': dict,
            'value': {
                'root': {
                    'valueType': dict,
                    'value': {
                        'path': {
                            'valueType': str
                        }
                    }
                },
                'sub': {
                    'valueType': list,
                    'each': {
                        'valueType': dict,
                        'value': {
                            'route': {
                                'valueType': str
                            },
                            'path': {
                                'valueType': str
                            }
                        }
                    }
                }
            }
        },
        'file_type': {
            'valueType': dict,
            'value': {
                'image': {
                    'valueType': list,
                    'each': {
                        'valueType': dict,
                        'value': {
                            'name': {
                                'valueType': str
                            },
                            'mime': {
                                'valueType': str
                            }
                        }
                    }
                },
                'audio': {
                    'valueType': list,
                    'each': {
                        'valueType': dict,
                        'value': {
                            'name': {
                                'valueType': str
                            },
                            'mime': {
                                'valueType': str
                            }
                        }
                    }
                },
                'other': {
                    'valueType': list,
                    'each': {
                        'valueType': dict,
                        'value': {
                            'name': {
                                'valueType': str
                            },
                            'mime': {
                                'valueType': str
                            }
                        }
                    }
                }
            }
        }
    }
}


def errorPathNotExists(owner: str, path: str):
    logger.error('{0}: path "{1}" not exists!!!'.format(owner, path))


def errorLostFields(owner: str, fields: Iterable):
    logger.error('{0} lost fields: {1}'.format(owner, str(fields)))


def errorUnknownFields(owner: str, fields: Iterable):
    logger.error('Unknown fields in {0}: {1}'.format(owner, str(fields)))


def errorFieldValueType(owner: str, correctType, errorType):
    logger.error('Type of {0} is incorrect, expected {1} instead of {2}'.format(
        owner, str(correctType), str(errorType)))


def getDiffBetweenKeys(base: set, d: dict) -> list:
    """
    获取两个列表中的不同元素，k1 是基准，k2 是需要获取的

    :param base: 基准
    :param d: 比较
    :return: [丢失的元素, 多出的元素]
    """
    com = set(d.keys())
    return [base - com, com - base]


def checkFields(owner: str, dictObj: dict, correctFields: list, essential: bool = True) -> bool:
    """
    检查各个字段名和值的类型是否正确

    :param owner: 字段拥有者的名字
    :param dictObj: 字段拥有者对象
    :param correctFields: 正确的字段
    :param essential: 该字段是否是必须的
    :return:
    """
    ret = True
    diff = getDiffBetweenKeys(set(correctFields), dictObj)
    if diff[0]:
        errorLostFields(owner, diff[0])
        ret = False
    if diff[1]:
        errorUnknownFields(owner, diff[1])
        ret = False
    return ret


def checkRole(owner: str, role: dict, dst) -> bool:
    """
    检查 json 语法

    :param owner:
    :param role:
    :param dst:
    :return:
    """
    valueType = role['valueType']
    if type(dst) != valueType:
        errorFieldValueType(owner, valueType, type(dst))
        return False
    if 'value' not in role.keys() and valueType != list:
        return True
    if valueType == dict:
        value = role['value']
        if not checkFields(owner, dst, list(value.keys())):
            return False
        for key in value.keys():
            if not checkRole('{0}.{1}'.format(owner, key), value[key], dst[key]):
                return False
    elif valueType == list:
        for i in range(len(dst)):
            if not checkRole('{0}[{1}]'.format(owner, i), role['each'], dst[i]):
                return False

    return True


def check(config: dict) -> bool:
    if not checkRole('config', ConfigRole, config):
        return False

    # 所有路径和路由不要以 '/' 结尾
    if config['server']['proxy']['url'][-1] == '/':
        config['server']['proxy']['url'] = config['server']['proxy']['url'][:-1]
    if config['route_path']['root']['path'][-1] == '/':
        config['route_path']['root']['path'] = config['route_path']['root']['path'][:-1]
    if 'sub' not in config['route_path'].keys():
        config['route_path']['sub'] = []
    for defSub in var.DefaultSubRoutes:  # 添加默认资源路由
        config['route_path']['sub'].append(defSub)
    for sub in config['route_path']['sub']:
        if sub['route'][-1] == '/':
            sub['route'] = sub['route'][:-1]
        if sub['path'][-1] == '/':
            sub['path'] = sub['path'][:-1]
    l = len(config['hidden']['dirs'])
    for i in range(l):
        if config['hidden']['dirs'][i][-1] == '/':
            config['hidden']['dirs'][i] = config['hidden']['dirs'][i][:-1]

    ret = True
    # 检查 日志路径、路由路径是否存在
    if config['log']['en'] and not os.path.exists(config['log']['path']):
        errorPathNotExists('config.log.path', config['log']['path'])
        ret = False
    if config['route_path']['root']['path'] != const.SpecialPath \
            and not os.path.exists(config['route_path']['root']['path']):
        errorPathNotExists('config.route_path.root.path', config['route_path']['root']['path'])
        ret = False
    for i in range(len(config['route_path']['sub'])):
        sub = config['route_path']['sub'][i]
        if not os.path.exists(sub['path']):
            errorPathNotExists('config.route_path.sub[{0}].path'.format(i), sub['path'])
            ret = False

    if ret:
        if config['server']['proxy']['en']:
            var.PROXY_URL = config['server']['proxy']['url']
        else:
            var.PROXY_URL = ''

    return ret
